﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Collections;
using System.Xml.Xsl;
using Grishko.SerilizeTools;
using Grishko.DataTier.MetaInfo;

namespace Grishko.DataTier
{
    /// <summary>
    /// Состояние объекта 
    /// </summary>
    public enum EntityState { Updated, Deleted, Loaded, Append } ;
         
    public  class Entity : ReflectionItem,IEntity
    {
        private string _HashSign = string.Empty; //Первоначльное состояние объекта  - см.EntityState State

        /// <summary>
        /// Конструктор 
        /// </summary>
        protected   Entity()
        {
            Init();
        }

        #region Property

        /// <summary>
        /// Включение выключение карты типов - если карта типов выключена - объект создается - четко того типа который указан 
        /// в TypeName 
        /// </summary>
        [EntityNonSerialized]
        public virtual  bool TypeMapperOn
        {
            get{return true;}
        }
        /// <summary>
        /// Состояние объекта.         
        /// </summary>
        [EntityNonSerialized]
        public EntityState State
        {
            get
            {
                string s = GetHash();
                if (String.IsNullOrEmpty(_HashSign)) return EntityState.Append;
                if (_HashSign.Equals(s)) return EntityState.Loaded;
                if (_HashSign.ToString().Equals(EntityState.Deleted.ToString())) return EntityState.Deleted;
                return EntityState.Updated;

            }

        }

        #endregion

        #region Protected
        
        /// <summary>
        /// Получить сокращенный граф объекта 
        /// </summary>
        /// <returns></returns>
        internal string GetHash()
        {
            return ToXml(false);
        }

        /// <summary>
        /// Установить переменную _HashSign
        /// </summary>
        public void SetHash()
        {
            _HashSign = GetHash();
        }

        #endregion
        

        #region Static
        /// <summary>
        /// Создает бизнес объект и десирилизует его из строки XML
        /// </summary>
        /// <param name="XmlString">      /// 
        /// </param>        
        /// <returns></returns>
        public static Entity GetEntity(string XmlString)
        {
            try
            {
                //считываем xml
                XmlDocument d = new XmlDocument();
                d.LoadXml(XmlString);
                // запускаем рекурсию по структуре объекта
                Entity NewObj = (Entity)ReflectionItem.CreateInstance(d.FirstChild);
                NewObj.ReadXml(d.FirstChild);
                return NewObj;
            }
            catch (Exception ex)
            {
                throw new Exception("Не могу десерилизовать объект", ex);
            }
        }

        /// <summary>
        /// Создает объект из XmlReader 
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        internal static Entity GetEntity(XmlReader reader)
        {
            XmlDocument d;
            try
            {
                //считываем xml
                d = new XmlDocument();
                d.Load(reader);
            }
            catch (Exception ex)
            {
                throw new Exception("Ошибка чтения XML", ex);
            }

            try
            {
                // запускаем рекурсию по структуре объекта
                Entity NewObj = (Entity)ReflectionItem.CreateInstance(d.FirstChild);
                NewObj.ReadXml(d.FirstChild);
                return NewObj;
            }
            catch (Exception ex)
            {
                throw new Exception("Не могу создать объект ", ex);
            }

        }

        /// <summary>
        /// Серилизует объект в строку 
        /// Full- выдает полный граф включая элементы отвечающие интерфейсу ISqlObject
        /// False- объекты с интерфейсом ISqlObject - пишутся - как ID и сборка 
        /// </summary>
        /// <returns>Строка XML</returns>
        public string ToXml(bool Full)
        {
            string sTagName = TagName;
            try
            {
                StringBuilder sb;
                MemoryStream ms = new MemoryStream();
                sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
                XmlTextWriter w = new XmlTextWriter(sw);
                w.Formatting = Formatting.Indented;
                w.Indentation = 3;
                this.WriteXml(w, TagName, Full);
                w.Close();
                return sb.ToString();
            }
            catch (Exception ex)
            {
                throw new Exception("Не могу cерилизовать объект", ex);
            }

        }
        #endregion


        #region Virtual
        /// <summary>
        /// Виртуальный метод - вызывается в конструкторе - фактически событие OnInit для наследников.        
        /// </summary>
        public virtual void Init()
        {

        }
  
        /// <summary>
        /// Сокращенный граф 
        /// </summary>
        [EntityNonSerialized]
        public virtual string ImAsXML 
        {
            get { return ToXml(false); }
        }

        /// <summary>
        /// IStoredObject        
        /// </summary>
        /// <param name="sParams"></param>
        /// <returns></returns>
        internal  virtual string GetXPatch(string[] sParams)
        {
            return string.Empty;
        }

        /// <summary>
        /// (IStoredObject)
        /// </summary>
        internal virtual SrchInfo GetSrchInfo(string RootID)
        {
            return null;
        }

        /// <summary>
        /// (IStoredObject)
        /// </summary>
        internal  virtual void PopulateSrchItemes(SrchInfo res, Entity obj, string RootId, string XPatch)
        {         
        }

        /// <summary>
        /// Поисковая информация. (IStoredObject)
        /// </summary>
        [EntityNonSerialized]
        public virtual SrchInfo SrchInfoVal
        {
            get
            {
                return null;
            }

        }


        #endregion

        #region ISerializableItem Members

        /// <summary>
        /// Вернуть XML схему объекта
        /// </summary>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }

      

        /// <summary>
        /// Вернуть ноду по имени.
        /// </summary>
        internal XmlNode GetNodeByName(XmlNode Node, string NodeName)
        {
            try
            {
                foreach (XmlNode item in Node.ChildNodes)
                {
                    if (item.Name.Equals(NodeName)) return item;
                }
                return null;
            }
            catch
            {
                throw;
            }
        }


        /// <summary>
        /// Десерилизовать объект из  XML ноды.
        /// </summary>
        /// <remarks>Алоритм. Пройтись по полям объекта - инициализировать поля объекта значеними одноименных полей ноды.</remarks>
        public virtual void ReadXml(System.Xml.XmlNode Node)
        {
            string s = string.Empty;
            Entity NewObj = null;
            if (null == Node) return;
            try
            {
                //string sAssmblyName =string .Empty ;
                //string sItemAssemblyType = string .Empty;
                //Type MyType=null;

                //if (Node.Attributes[SerilizeContext.AssemblyPref] != null)
                //{
                //    sAssmblyName = Node.Attributes[SerilizeContext.AssemblyPref].Value;
                //    sItemAssemblyType = Node.Attributes[SerilizeContext.TypePref].Value;
                //    MyType = ReflectionItem.MyGetType(sAssmblyName, sItemAssemblyType);
                //}

                //if (null == MyType) 
                //    MyType = this.GetType();

                Type MyType = this.GetType();

                PropertyInfo[] MyFields = MyType.GetProperties();
                //поля узла
                foreach (PropertyInfo inf in MyFields)
                {
                    
                    s = inf.Name;                    
                    ////Если свойство входит в список не серилизуемых свойств - пропускаем
                    //if (this.PropIsNonSerilize(inf.Name)) continue;

                    //читаем перечисления
                    if (IsEnum(inf))
                    {
                        XmlAttribute vEnum = Node.Attributes[inf.Name];
                        if (vEnum == null) continue;
                        object r = Enum.Parse(inf.PropertyType, vEnum.Value);
                        inf.SetValue(this, r, null);
                        continue;
                    }


                    // если это бизнес объект и для него есть значения в xml - десерилизуем его
                    if (PropertyIsSerializableItem(inf.PropertyType))
                    {
                        XmlNode ChildNode = GetNodeByName(Node, inf.Name);
                        if (!(null == ChildNode))
                        {
                            Entity I=null;
                            //Если - объектное свойство создается по умолчанию используем тип свойства  заданный носителем 
                            object PropValue = inf.GetValue(this, null);
                            if (null == PropValue)
                            {
                                Type Tr =null;
                                if (TypeMapperOn)
                                    // Получаем  тип указанный в ноде - если его не существует - игнорируем заполнение бизнесобъекта
                                    Tr = MyGetType(ChildNode.Attributes[SerilizeContext.AssemblyPref].Value,
                                                        ChildNode.Attributes[SerilizeContext.TypePref].Value,
                                                        ChildNode.Attributes[SerilizeContext.TagNamePref].Value);
                                else
                                    Tr = MyGetType(ChildNode.Attributes[SerilizeContext.AssemblyPref].Value,
                                                    ChildNode.Attributes[SerilizeContext.TypePref].Value);

                                if (null == Tr)
                                    continue;

                                // сформировать бизнес объект и заполнить его 
                                NewObj = (Entity)Activator.CreateInstance(Tr, null);


                                PropValue = NewObj;
                                inf.SetValue(this, PropValue, null);

                            }
                            I = (Entity)PropValue;
                            I.ReadXml(ChildNode);

                        }
                        continue;
                    }


                    // выставляем значение атрибута
                    XmlAttribute fValue = Node.Attributes[inf.Name];
                    if (fValue == null) continue;
                    if (string.IsNullOrEmpty(fValue.Value)) continue;

                    object val = null;
                    Type t = inf.PropertyType;
                    val = fValue.Value.ToString();

                    if (t.Equals(typeof(Decimal))) val = decimal.Parse(fValue.Value);
                    if (t.Equals(typeof(DateTime))) val = DateTime.Parse(fValue.Value);
                    if (t.Equals(typeof(int))) val = int.Parse(fValue.Value);
                    if (t.Equals(typeof(bool))) val = bool.Parse(fValue.Value);
                    if (t.Equals(typeof(double))) val = double.Parse(fValue.Value);

                    if (inf.CanWrite)
                        inf.SetValue(this, val, null);
                }
            }

            catch (Exception ex)
            {                
                throw new Exception
                    (string.Format("Не могу серилизовать объект из ноды {0}", Node.InnerXml), ex);
            }
        }

        public  virtual void ReadXml(System.Xml.XmlNode Node, string Assembly)
        {
        }

        public virtual void ReadXml(string sNode)
        {
            XmlDocument d = new XmlDocument();
            try
            {
                TextReader stringReader = new StringReader(sNode);
                XmlTextReader r = new XmlTextReader(stringReader);
                d.Load(r);
                if (null == d.FirstChild) return;
                ReadXml(d.FirstChild);
            }
            finally
            {

            }

        }

        /// <summary>
        /// Пишет объект в виде XML строчки в передаваемый ему Writer
        /// </summary>
        internal virtual void WriteXml(System.Xml.XmlWriter writer, string ObjectName, bool Full)
        {
        }

        internal virtual void WriteShortXml(System.Xml.XmlWriter writer, string ObjectName)
        { 
        }

        


        /// <summary>
        /// Элементарные типы
        /// </summary>
        internal List<PropertyInfo> AttPropertiesList
        {
            get
            {
                return ReflectionItem.GetSerisableAttProperties (this.GetType());              
            }
        }

        
        /// <summary>
        /// / Набор свойств Entity поддерживающих серилизацию, 
        /// </summary>
        internal List<PropertyInfo> EntityPropertiesList
        {
            get
            {             
                Type[] Incl = {};
                Type[] Excl= { typeof(EntityNonSerialized) };
                return ReflectionItem.GetSerisablePropertyList(this.GetType(), typeof(Entity), Incl, Excl);            
            }
        }
    
        internal List<PropertyInfo> EntitySrchInfoPropertiesList
        {
            get
            {
                Type[] Incl = { typeof(SrchItemInfo) };
                Type[] Excl = {};
                return ReflectionItem.GetSerisablePropertyList(this.GetType(), typeof(Entity), Incl, Excl);
            }
        }


        /// <summary>
        /// Перечисления
        /// </summary>
        internal List<PropertyInfo> EnumPropertiesList
        {
            get
            {
                List<PropertyInfo> Result = new   List<PropertyInfo>();
                PropertyInfo[] MyFields = this.GetType().GetProperties();

                foreach (PropertyInfo inf in MyFields)
                {

                    if (ReflectionItem.ImEntityNonSerialized(inf))
                        continue;

                    if (IsEnum(inf))
                        Result.Add(inf);


                }
                return Result;

           }
        }

        #endregion

        #region IEntity Members


        public virtual void Save()
        {


        }


        public virtual void SaveWitchChild()
        {
            
        }

        public virtual void Update(){}

        public virtual void UpdateWitchChild() { }


        public virtual void Save(string ConnectionString)
        {


        }

        /// <summary>
        /// прогуржает объект и объектные свойства 
        /// </summary>
        public virtual void Load()
        {

        }

        /// <summary>
        /// прогуржает объектные свойства 
        /// </summary>
        public virtual void PropertyLoad()
        { 
        }

        /// <summary>
        /// Выставляет свойство HashSign = EntityState.Deleted в зависимости от этого свойства для объектов Isql Object - вызывается либо Save, либо Delete
        /// </summary>
        public virtual void DestroyMySelf()
        {
            _HashSign = EntityState.Deleted.ToString();
        }


        #endregion


        /// <summary>
        /// Указывает поддерживает ли экземпляр интерфейс ISqlSignature
        /// </summary>        
        [EntityNonSerialized]
        public bool ImSql
        {
            get
            {

                Type[] interf = GetType().GetInterfaces();
                return ImSupprotType(interf, typeof(ISqlSignature));
            }

        }

        /// <summary>
        /// Указывает поддерживает ли экземпляр интерфейс ISqlSignature
        /// </summary>        
        [EntityNonSerialized]
        public bool ImFile
        {
            get
            {
                Type[] interf = GetType().GetInterfaces();
                return ImSupprotType(interf, typeof(IFileSignature));
            }

        }

        /// <summary>
        /// Таg под которым будет серилизован объект, во умолчанию GetType().Name
        /// </summary>   
        public   string TagName
        {            
            get
                {
                    //Если нет у класса  [TagNameInfo]
                    Type[] Incl = { typeof(TagNameInfo) };
                    Type MyType=this.GetType();
                    if (ReflectionItem.MemberInAttType(MyType, Incl))
                    {
                        object[] arr = MyType.GetCustomAttributes(typeof(TagNameInfo), true);
                        TagNameInfo _atr = (TagNameInfo)arr[0];
                        return _atr.TagName; 
                    };

                        return MyType.Name; 
                }
        }

       
       
    }

}
